﻿using System;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.ComponentModel;
using System.Drawing.Design;
using System.Reflection;
using System.ComponentModel.Design;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Threading;

namespace ControlLibrary.ComponentModel {
    /// <summary>
    /// LookupField custom type editor for design-time environment, 
    /// where typeof(FormQueryBase).IsAssignableFrom(type)
    /// </summary>
    internal class QueryEditor : UITypeEditor {
        private ITypeDescriptorContext _context = null;
        private IServiceProvider _provider = null;
        private List<ComponentDescription> _componentList = null;

        /// <summary>
        /// Displays an ellipsis (...) button to start a modal dialog box.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
            return UITypeEditorEditStyle.Modal;
        }

        /// <summary>
        /// Edits the value of the specified object using the specified service provider and context.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        /// <param name="provider">A service provider object through which editing services can be obtained.</param>
        /// <param name="value">The object to edit the value of. </param>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
            if (context != null && provider != null) {
                this._context = context;
                this._provider = provider;

                var dlgProgress = new ClassLibrary.ProgressDialog(true, longOperation);
                if (dlgProgress.ShowDialog() == DialogResult.OK) {
                    var result = showEditorDialog(provider, _componentList);
                    if (result.IsDialogResultOK) {
                        value = result.Value;
                    }
                }
            }
            return value;
        }

        /// <summary>
        /// No special thumbnail will be shown for the grid.
        /// </summary>
        /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
        public override bool GetPaintValueSupported(System.ComponentModel.ITypeDescriptorContext context) {
            return false;
        }

        #region Private Methods

        /// <summary>
        /// Show a FormPickerEditor dialog, retrieving the result value.
        /// </summary>
        private FormPickerEditorResult showEditorDialog(IServiceProvider provider, List<ComponentDescription> componentList) {
            FormPickerEditorResult result = new FormPickerEditorResult();
            result.IsDialogResultOK = false;
            try {
                IWindowsFormsEditorService editorService = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
                if (editorService != null) {
                    FormPickerEditor selectionControl = new FormPickerEditor();
                    selectionControl.FillDataGrid(componentList);
                    if (editorService.ShowDialog(selectionControl) == DialogResult.OK) {
                        result.IsDialogResultOK = true;
                        result.Value = selectionControl.Value;
                    }
                }
            } catch { }
            return result;
        }

        /// <summary>
        /// Get the types that where lookupFieldBindingSourceType and formQueryBindingSourceType match.
        /// </summary>
        private List<Type> getDesiredTypes(Type lookupFieldBindingSourceType, List<Type> assemblyTypes) {
            var types = new List<Type>();
            try {
                if (lookupFieldBindingSourceType != null) {
                    foreach (Type type in assemblyTypes) {
                        Type formQueryBindingSourceType = getFormQueryBindingSourceType(type);
                        if (formQueryBindingSourceType != null) {
                            if (lookupFieldBindingSourceType.FullName == formQueryBindingSourceType.FullName) {
                                types.Add(type);
                            }
                        }
                    }
                }
            } catch { }
            return types;
        }

        /// <summary>
        /// Get the types defined in the RootComponent.
        /// </summary>
        private List<Type> getAssemblyTypes(IServiceProvider provider) {
            var types = new List<Type>();
            try {
                IDesignerHost host = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
                ITypeResolutionService resolution = (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
                string assemblies = "";

                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a=>a.FullName.Contains("WinFormApp"))) {
                    assemblies += assembly.FullName + " -- ";
                }
                MessageBox.Show(assemblies);
                Type rootComponentType = resolution.GetType(host.RootComponentClassName);
                types = rootComponentType.Assembly.GetTypes().ToList();
            } catch { }
            return types;
        }

        /// <summary>
        /// Get the LookupField instance
        /// </summary>
        private LookupField getLookupField(ITypeDescriptorContext context) {
            LookupField lookup = null;
            try {
                //smart-tag
                if (context.Instance.GetType() == typeof(LookupFieldActionList)) {
                    var actionList = (LookupFieldActionList)context.Instance;
                    var flags = BindingFlags.Instance | BindingFlags.NonPublic;
                    var control = actionList.GetType().GetField("LinkedControl", flags);
                    lookup = (LookupField)control.GetValue(actionList);
                    //properties-window
                } else {
                    lookup = (LookupField)context.Instance;
                }
            } catch { }
            return lookup;
        }

        /// <summary>
        /// Get the DataSource type defined in LookupField. It expects that the DataSource is a 
        /// BindingSource, getting in reality the BindingSource.DataSource type.
        /// </summary>
        private Type getLookupFieldBindingSourceType(LookupField lookupField) {
            Type lookupFieldBindingSourceType = null;
            try {
                if (lookupField != null) {
                    if (lookupField.DataSource != null) {
                        Type lookupFieldDataSourceType = lookupField.DataSource.GetType();

                        if (lookupFieldDataSourceType == typeof(BindingSource)) {
                            lookupFieldBindingSourceType = (Type)((BindingSource)lookupField.DataSource).DataSource;
                        }
                    }
                }
            } catch { }
            return lookupFieldBindingSourceType;
        }

        /// <summary>
        /// Get the DataSource type defined in the FormQueryBase property, BindingSource.
        /// </summary>
        private Type getFormQueryBindingSourceType(Type type) {
            Type formQueryBindingSourceType = null;
            try {
                if (typeof(FormQueryBase).IsAssignableFrom(type)) {
                    if (type.GetConstructor(new Type[] { typeof(ObjectContext) }) != null) {
                        PropertyInfo bindingSourceInfo = typeof(FormQueryBase).GetProperty(LibraryConstants.FormQueryBase_BindingSource_PropertyName, BindingFlags.Public | BindingFlags.Instance);
                        if (bindingSourceInfo != null) {
                            object value = bindingSourceInfo.GetValue(Activator.CreateInstance(type, new object[] { null }), null);
                            if (value != null) {
                                if (value.GetType() == typeof(BindingSource)) {
                                    formQueryBindingSourceType = (Type)((BindingSource)value).DataSource;
                                }
                            }
                        }
                    }
                }
            } catch { }
            return formQueryBindingSourceType;
        }
        
        private void longOperation(object sender, DoWorkEventArgs e) {
            Type lookupFieldBindingSourceType = getLookupFieldBindingSourceType(getLookupField(this._context));

            this._componentList = new List<ComponentDescription>();
            foreach (Type type in getDesiredTypes(lookupFieldBindingSourceType, getAssemblyTypes(this._provider))) {
                this._componentList.Add(new ComponentDescription(type));
            }

            //remove this in real world scenarios
            //Thread.Sleep(900);
        }

        #endregion
    }
}

